#!/usr/bin/python
# -*- coding: utf-8 -*-


# cmap2owl -- Helper application to convert from concept maps to OWL ontologies
# Copyright (c) 2008-2013  Rodrigo Rizzi Starr
#  
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#  
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#  
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.


'''
A class that draws cmaps

@author: Rodrigo Rizzi Starr
@copyright: Copyright © 2008-2013 Rodrigo Rizzi Starr
@license: MIT License
@contact: rodrigo.starr@gmail.com
'''


from __future__ import division
from math import pi, atan2, tan

# Project imports
from log import log
log.debug("Loaded...")


class GraphicalElement(object):
    "A basic graphical element that is able to draw itself in a context"
    
    @staticmethod
    def factory(element):
        """Returns a graphical representation for element of the right kind"""
        if element == "Concept":
            return Concept
        elif element == "LinkingPhrase":
            return LinkingPhrase
    
    def __init__(self, x, y, width, height, owner):
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.owner = owner
        self.canvas = None
        self.hasFocus = False

    def __str__(self):
        """Imprime uma representação deste elemento."""
    
        rep = u"%s graphical element.\n" % self.__class__.__name__
    
        rep += (u'        x: %s\n'
                 '        y: %s\n'
                 '    width: %s\n'
                 '   height: %s\n' % (self.x, self.y, self.width, self.height))
        return rep
        
    
    def getId(self):
        "Returns the id of the element"
        return self.owner.id
    id = property(getId)
        
    def contains(self, x, y):
        "Returns true if (x,y) is contained within this element"
        return (x >= self.x and x <= self.width+self.x) and \
               (y >= self.y and y <= self.height+self.y)
    
    def draw(self):
        """Draw this element on the canvas. In fact, delegate the drawing to
          specialized functions, one if the element has focus, another if it
          hasn't"""
        if not self.hasFocus:
            self.drawWithoutFocus()
        else:
            self.drawWithFocus()
      
    def getFocus(self):
        "Getter for the value of focus"
        return self.hasFocus
      
    def setFocus(self, focus):
        "Setter for the value of focus. If it has changed, force a redraw"
        if focus != self.hasFocus:
            self.hasFocus = focus
            self.canvas.redraw_canvas()
        
    focus = property(getFocus, setFocus)
        
    def drawWithoutFocus(self):
        """Draw the standard representation of this element. Should be
            implemented by clients"""
        raise NotImplementedError
      
    def drawWithFocus(self):
        """Draw the representation of this element when focused. Should be
            implemented by clients"""
        raise NotImplementedError
      
    def getCenter(self):
        "Return the center-point of element el (x,y)"
        return (self.x + self.width/2, self.y + self.height/2)
    
    def getBoundingBoxCross(self, x, y, spacing = 4):
        """Finds the point where a line from x,y crosses the bounding box of
          this element, plus a margin (spacing)"""
        c_x, c_y = self.getCenter()
        angle = atan2(y-c_y, x-c_x)
        
        # These values will be returned
        x = 0
        y = 0
        
        # The limit angles are on the edge of the rectangle.
        #  We must calculate them
        lAngle = atan2(self.height, self.width)
        
        
        if angle > -lAngle and angle < lAngle:
            # Right side.
            x = self.x + self.width + spacing
            y = c_y + self.width/2*tan(angle)
        elif angle > lAngle and angle < pi-lAngle:
            # Bottom side
            x = c_x + self.height/2*tan(pi/2-angle)
            y = self.y + self.height + spacing
        elif angle > pi-lAngle or angle < -pi+lAngle:
            # Left side
            x = self.x - spacing
            y = c_y + self.width/2*tan(pi - angle)
        else:
            # Top side
            x = c_x - self.height/2*tan(pi/2-angle)
            y = self.y - spacing
        
        return (x, y)
        
        
class Concept(GraphicalElement):
    "Draws a concept"
    
    def drawWithoutFocus(self):
        "Draws a rectangle with a concept label inside"
        
        label = self.owner.label
        x = self.x
        y = self.y
        width = self.width
        height = self.height
        color = (0.929411764, 0.95686, 0.9647058823)
        self.canvas.drawBox(x, y, width, height, color, True)
        self.canvas.drawLabel(x, y, width, height, label)
        
      
    def drawWithFocus(self):
        "Focus the rectangle"
        label = self.owner.label
        x = self.x
        y = self.y
        width = self.width
        height = self.height
        color = (0.729411764, 1.0, 0.7647058823)
        self.canvas.drawBox(x, y, width, height, color, True)
        self.canvas.drawLabel(x, y, width, height, label)
        
        
class LinkingPhrase(GraphicalElement):
    "Draws a linking phrase"
    
    def drawWithoutFocus(self):
        "Draws a rectangle with a concept label inside"
        self.drawArcs()
        
        label = self.owner.label
        x = self.x
        y = self.y
        margin = 3
        width = self.width
        height = self.height
        color = (1.0, 1.0, 1.0)
        self.canvas.drawBox(x-margin, y-margin,
                            width+2*margin, height+2*margin, color)
        self.canvas.drawLabel(x-margin, y-margin,
                              width+2*margin, height+2*margin, label)
        
    def drawArcs(self):
        """Draws a line from the concept tied to the arc and them to the other
          concepts"""
        
        c_x, c_y = self.getCenter()
        
        for el in self.owner.connectionsFrom():
            x, y = el.graphicalProperties.getCenter()
            conn = self.owner.graph.connectionMap[ (el, self.owner) ] 
            controlPoints = conn.graphicalProperties.controlPoints
            if len(controlPoints) == 0:
                x_s, y_s = self.getBoundingBoxCross(x, y)
                x, y = el.graphicalProperties.getBoundingBoxCross(c_x, c_y, 0)
            else:
                fx, fy = controlPoints[0]
                lx, ly = controlPoints[-1]
                x_s, y_s = self.getBoundingBoxCross(fx, fy)
                x, y = el.graphicalProperties.getBoundingBoxCross(lx, ly, 0)
            conn.graphicalProperties.draw(x_s, y_s, x, y,
                                         self.canvas, arrow = False)
          
        for el in self.owner.connectionsTo():
            x, y = el.graphicalProperties.getCenter()
            x_s, y_s = self.getBoundingBoxCross(x, y, 0)
            
            conn = self.owner.graph.connectionMap[ (self.owner, el) ] 
            controlPoints = conn.graphicalProperties.controlPoints
            if len(controlPoints) == 0:
                x, y = el.graphicalProperties.getBoundingBoxCross(c_x, c_y)
            else:
                lx, ly = controlPoints[-1]
                x, y = el.graphicalProperties.getBoundingBoxCross(lx, ly)
            conn.graphicalProperties.draw(x_s, y_s, x, y,
                                         self.canvas, arrow = True)
            
      
    def drawWithFocus(self):
        "The focus is a box around the label. Too poor. Must be improved"
        
        self.drawArcs()
        
        label = self.owner.label
        x = self.x
        y = self.y
        margin = 3
        width = self.width
        height = self.height
        color = (0.729411764, 1.0, 0.7647058823)
        self.canvas.drawBox(x-margin, y-margin,
                            width+2*margin, height+2*margin, color)
        self.canvas.drawLabel(x-margin, y-margin,
                              width+2*margin, height+2*margin, label)
        
class Connection:
    "Draws a connection between a concept and a linking phrase"
    
    def __init__(self, owner, cType = 'vector'):
        self.owner = owner
        self.type = cType
        self.controlPoints = []
    
    def draw(self, xFrom, yFrom, xTo, yTo, canvas, arrow = False):
        "Draws this connection"
        
        if self.type == 'bezier-4pt':
            x2, y2 = self.controlPoints[0]
            x3, y3 = self.controlPoints[1]
            canvas.drawCurve(xFrom, yFrom,
                             x2, y2,
                             x3, y3,
                             xTo, yTo, arrow = arrow)
                
        elif self.type == 'bezier-3pt':
            x2, y2 = self.controlPoints[0]
            x3, y3 = self.controlPoints[0]
            canvas.drawCurve(xFrom, yFrom,
                             x2, y2,
                             x3, y3,
                             xTo, yTo, arrow = arrow)
        
        elif self.type == 'straight': # Ignores control points
            canvas.drawLine(xFrom, yFrom, xTo, yTo, arrow=arrow)
        
        else:  # self.type == 'vector' or splines (we don't know how to draw)
            for x, y in self.controlPoints:
                canvas.drawLine(xFrom, yFrom, x, y)
                xFrom, yFrom = x, y
              
            canvas.drawLine(xFrom, yFrom, xTo, yTo, arrow=arrow)
            
      
